/*******************************************************************************
 * Copyright (c) 2007 University of Oslo.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     University of Oslo - initial API and implementation
 *******************************************************************************/

package com.cea.papyrus.diagram.sequence.sedi.model.command;

import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.createUniqueName;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getCoveredBys;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getFragments;
import static com.cea.papyrus.diagram.sequence.sedi.model.util.ModelUtil.getOperands;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.gef.commands.Command;
import org.eclipse.uml2.uml.CombinedFragment;
import org.eclipse.uml2.uml.InteractionConstraint;
import org.eclipse.uml2.uml.InteractionFragment;
import org.eclipse.uml2.uml.InteractionOperand;
import org.eclipse.uml2.uml.UMLFactory;

/**
 * Command to combine two InteractionOperands into one, removing the second.
 * <p>
 * The following attributes must be set for the command to complete successfully:
 * </p>
 * <ul>
 * <li>InteractionOperand
 * <li>Positions
 * </ul>
 * <p>
 * The following attributes may be set for the command to complete successfully:
 * </p>
 * <ul>
 * <li>LowerOperand - if set, this operand will be used as the lower operand. Ordinarily,
 * a new operand is created.
 * </ul>
 * 
 * @author frankd@ifi.uio.no
 */
public class SplitInteractionOperandCommand extends Command {

    private InteractionOperand upperOperand;
    private InteractionOperand lowerOperand;
    private Collection<LifelinePosition> positions;
    private InteractionConstraint constraint;

    public void setInteractionOperand(final InteractionOperand operand) {
        this.upperOperand = operand;
    }

    /**
     * Sets the constraint.
     * 
     * @param constraint
     *            the constraint to set
     */
    public void setConstraint(InteractionConstraint constraint) {
        this.constraint = constraint;
    }

    public void setLowerOperand(final InteractionOperand operand) {
        this.lowerOperand = operand;
    }

    public void setPositions(final Collection<LifelinePosition> positions) {
        this.positions = positions;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void execute() {
        final CombinedFragment cfragment = (CombinedFragment) upperOperand.getOwner();

        if (lowerOperand == null) {
            lowerOperand = UMLFactory.eINSTANCE.createInteractionOperand();
            lowerOperand.setName(createUniqueName(cfragment.getOperands(), "Operand_"));
        }
        lowerOperand.setGuard(this.constraint);
        final List<InteractionOperand> operands = getOperands(cfragment);
        operands.add(operands.indexOf(upperOperand) + 1, lowerOperand);

        final List<InteractionFragment> upperFragments = getFragments(upperOperand);
        final List<InteractionFragment> toMove =
                new ArrayList<InteractionFragment>(upperFragments.size());

        for (LifelinePosition lp : positions) {
            int index = lp.getPosition();
            if (index == -1)
                continue;

            List<InteractionFragment> coveredBys = getCoveredBys(lp.getLifeline());
            while (index < coveredBys.size() && upperFragments.contains(coveredBys.get(index))) {
                toMove.add(coveredBys.get(index));
                index++;
            }
        }

        lowerOperand.getFragments().addAll(toMove);
    }

    public InteractionOperand getLowerOperand() {
        return lowerOperand;
    }

    @Override
    public void undo() {
        final CombinedFragment cfragment = (CombinedFragment) upperOperand.getOwner();
        final List<InteractionOperand> operands = getOperands(cfragment);
        final InteractionOperand lowerOperand = operands.get(operands.indexOf(upperOperand) + 1);

        final UnionInteractionOperandsCommand cmd = new UnionInteractionOperandsCommand();
        cmd.setInteractionOperand(lowerOperand);

        try {
            cmd.execute();
        } finally {
            cmd.dispose();
        }
    }

    @Override
    public void redo() {
        execute();
    }

    @Override
    public void dispose() {
        upperOperand = null;
        positions = null;
    }
}
